#ifndef BSHARPTREE_H_
#define BSHARPTREE_H_

#include "Node.h"
#include "KeyNode.h"
#include "Configuration.h"
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "../Conf/CodigosResultadosIndices.h"
#include "../Conf/CodigoArbol.h"
#include "../Model/ArchivoBinario.h"

using namespace std;

class BsharpTree
{
	private:

		Node* nodoActual;
		KeyNode::iterator iterClavesActual;
		Node* nodoRaiz;

	protected:

		unsigned short tamanioNodo; //Tamanio máximo (en bytes) de un nodo
		string nombreArchivo;

	public:

		BsharpTree(string nombreArchivo, unsigned short tamanioNodo);
		~BsharpTree();

		/*Header del Nodo*/
		struct HeaderNodo{
			unsigned int nivel;
			unsigned int refNodo;
			unsigned short espacioLibre;
		};
		ArchivoBinario *archivoBinario;

		void copiarClaveHoja(Clave* clave,string& puntero);
		void copiarClaveNoHoja(Clave* clave,string& puntero);

		Clave* leerClaveHoja(string &buffer)
		{
			string valor;
			string refRegistro;
			Utilitaria* utilitaria = new Utilitaria();
			unsigned int posicion = 0;
			unsigned int size = buffer.find_first_of('%');

			valor = buffer.substr(posicion, size);

			posicion = size;
			size = buffer.find_first_of('/');

			refRegistro = buffer.substr(posicion + 1, (size - (posicion + 1)));

			//Crear la clave
			return new Clave(valor, utilitaria->StringToInt(refRegistro));
		}

		Clave* leerClaveNoHoja(string &buffer)
		{
			string valor;
			string hijoDer;
			unsigned int refRegistro = 0;
			Utilitaria* utilitaria = new Utilitaria();
			int posicion = 0;
			int size = buffer.find_first_of('%');

			valor = buffer.substr(posicion, size);

			posicion = size;
			size = buffer.find_first_of('/');

			hijoDer = buffer.substr(posicion + 1, (size - (posicion + 1)));

			return new Clave(valor, refRegistro, utilitaria->StringToInt(hijoDer));
		}

		int leerNodo(unsigned int numeroBloque, Node* bloqueLeido);
		int escribirNodo(Node* bloqueNuevo);

		//Permite modificar la informacion de un nodo
		int escribirNodo(unsigned short numBloque, Node* bloqueModif);

		//Agrega una referencia en el archivo de nodos liberados al nodo que se quiere eliminar

		int eliminarNodo(unsigned short posicion);
		bool insertar(Clave* clave);
		bool eliminar(Clave* clave);
		Clave* buscar(Clave* clave);

		//Devuelve false si claveVieja no se encuentra insertada en el arbol. En caso contrario, la reemplaza por claveNueva
		//y devuelve true.
		int modificar(Clave* claveVieja, Clave* claveNueva);

		Node* getNodoActual(){
			return nodoActual;
		}

		void setNodoRaiz(Node* node){
			nodoRaiz = node;
		}

		static int getTamanioHeader()
		{
			return sizeof(HeaderNodo);
		}

		string getNombreArchivo()
		{
			return this->nombreArchivo;
		}

		void setNombreArchivo(string nombre)
		{
			this->nombreArchivo = nombre;
		}


		//Deja en nodoActual el primer nodo del set secuencial y al iterClavesActual en el primer elemento dentro de ese nodo.
		void primero();

		//Deja en nodoActual el nodo del set secuencial donde se encuentre la primer clave mayor o igual a la recibida por parámetro
		//y al iterClavesActual en dicho elemento.
		void mayorOIgual(Clave* clave);

		//Deja en nodoActual el nodo del set secuencial donde se encuentre la primer clave mayor a la recibida por parámetro
		//y al iterClavesActual en dicho elemento.
		void mayor(Clave* clave);

		//Devuelve la siguiente clave dentro del árbol. DEBE ser llamado después de primero(), sin
		//modificaciones dentro del árbol luego de ser llamado este método, para garantizar su buen
		//funcionamiento. Si no hay más elementos siguientes devuelve NULL.
		Clave* siguiente();

		bool vacio(){ return (this->nodoRaiz == NULL); }

		//Devuelve un nodo con la raiz del arbol
		Node* getRaiz();

	private:

		void insertarInterno(Node* &nodoDestino, char* codigo, Clave* claveInsertada);

	    void eliminarInterno(Node* &nodoTarget, char* codigo, Clave* claveEliminada);

	    Node* buscarPadre(Node* padre, Node* hijo, Clave* claveNodoHijo);

	    Node* buscarLugar(Clave* clave);

	    Node* buscarLugarRecursivo(Node* nodo, Clave* clave);

	    Clave* split(Node* nodoTarget);

	    //Mergea nodoConUnderflow con nodoHno; y con el separador si se trata de un nodo interno. No se encarga
	    //de modificar nada en el nodo padre, hay que hacerlo desde afuera.
	    void merge(Node* nodoIzq, Node* &nodoDer, Clave* separador);

	    bool puedeMerge(Node* nodoIzq, Node* nodoDer, Clave* separador);

	    void pasarClaveHaciaIzquierda(Node* nodoDestino, Node* nodoPadre, Node* nodoHnoDer, Clave* clavePadre);

	    void pasarMaximoPosibleHaciaIzquierda(Node* nodoDestino, Node* nodoPadre, Node* nodoHnoDer, Clave* &clavePadre);

	    void pasarClaveHaciaDerecha(Node* nodoDestino, Node* nodoPadre, Node* nodoHnoIzq, Clave* clavePadre);

	    void pasarMaximoPosibleHaciaDerecha(Node* nodoDestino, Node* nodoPadre, Node* nodoHnoIzq, Clave* &clavePadre);

};

#endif /* BSHARPTREE_H_ */
